home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Collections: Camelot
/
Camelot 078 (1990-06)(Swedish User Group of Amiga)(SE)(PD)[WB].zip
/
Camelot 078 (1990-06)(Swedish User Group of Amiga)(SE)(PD)[WB].adf
/
MSH
/
src
/
hanlock.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-06-17
|
18KB
|
752 lines
/*-
* $Id: hanlock.c,v 1.30 90/06/04 23:17:18 Rhialto Rel $
* $Log: hanlock.c,v $
* Revision 1.30 90/06/04 23:17:18 Rhialto
* Release 1 Patch 3
*
* HANLOCK.C
*
* The code for the messydos file system handler
*
* The Lock department. Takes care of operations on locks, and consequently,
* on directories.
*
* This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
* not be used or copied without a licence.
-*/
#include "dos.h"
#include "han.h"
#ifdef HDEBUG
# define debug(x) dbprintf x
#else
# define debug(x)
#endif
struct LockList *LockList; /* List of all locked files we have. Note
* this is not the same as all locks we
* have */
struct MSFileLock *RootLock; /* Lock on root directory */
struct MSFileLock *EmptyFileLock; /* 2nd result of MSLock() */
struct DirEntry FakeRootDirEntry = {
{ /* de_Msd */
"Unnamed ", /* msd_Name */
" ", /* msd_Ext */
ATTR_VOLUMELABEL, /* msd_Attributes */
{0}, /* msd_Pad1 */
0, /* msd_Time */
DATE_MIN, /* msd_Date, 1/1/80 */
0, /* msd_Cluster */
0 /* msd_Filesize */
},
ROOT_SEC, /* de_Sector */
0 /* de_Offset */
};
byte DotDot[1 + 8 + 3] = ".. ";
/*
* This routine compares a name in a directory entry with a given name
*/
int
CompareNames(dir, name)
register struct MsDirEntry *dir;
register byte *name;
{
if (dir->msd_Name[0] & DIR_DELETED_MASK)
return CMP_FREE_SLOT;
if (dir->msd_Name[0] == 0)
return CMP_END_OF_DIR; /* end of directory */
if (dir->msd_Attributes & ATTR_VOLUMELABEL)
return CMP_NOT_EQUAL;
if (strncmp(dir->msd_Name, name, 8 + 3))
return CMP_NOT_EQUAL;
if (dir->msd_Attributes & ATTR_DIRECTORY)
return CMP_OK_DIR;
return CMP_OK_FILE;
}
void
NextDirEntry(sector, offset)
register word *sector;
register word *offset;
{
if ((*offset += MS_DIRENTSIZE) >= Disk.bps) {
*offset = 0;
if (*sector >= Disk.datablock) {
/* Must be subdirectory */
*sector = NextClusteredSector(*sector);
debug(("NextClusteredSector: %d\n", *sector));
} else {
if (++*sector >= Disk.datablock) {
*sector = SEC_EOF;
}
}
}
/* else no more work needed */
}
/*
* Get the directory entry following the given one. If requested, we make
* the directory longer.
*/
struct DirEntry *
FindNext(previous, createit)
register struct DirEntry *previous;
int createit;
{
byte *sector;
word prevsec = previous->de_Sector;
NextDirEntry(&previous->de_Sector, &previous->de_Offset);
if (previous->de_Sector == SEC_EOF) {
error = ERROR_OBJECT_NOT_FOUND;
#ifndef READONLY
if (createit) {
if (prevsec < Disk.datablock - 1) { /* Should not be necessary */
previous->de_Sector = prevsec + 1;
} else if (prevsec >= Disk.datablock) {
previous->de_Sector = FindFreeSector(prevsec);
}
if (previous->de_Sector != SEC_EOF) {
sector = EmptySec(previous->de_Sector);
setmem(sector, (int) Disk.bps, 0);
MarkSecDirty(sector);
FreeSec(sector);
setmem(&previous->de_Msd, sizeof (previous->de_Msd), 0);
return previous;
}
}
#endif
} else if (sector = GetSec(previous->de_Sector)) {
CopyMem(sector + previous->de_Offset, &previous->de_Msd,
(long) MS_DIRENTSIZE);
OtherEndianMsd(&previous->de_Msd);
FreeSec(sector);
return previous;
}
return NULL;
}
#ifdef HDEBUG
void
PrintDirEntry(de)
struct DirEntry *de;
{
debug(("%d,%d ", de->de_Sector, de->de_Offset));
debug(("%.8s.%.3s attr:%x time:%x date:%x start:%x size:%lx\n",
de->de_Msd.msd_Name,
de->de_Msd.msd_Ext,
de->de_Msd.msd_Attributes,
de->de_Msd.msd_Time,
de->de_Msd.msd_Date,
de->de_Msd.msd_Cluster,
de->de_Msd.msd_Filesize
));
}
#endif
/*
* MakeLock makes a struct MSFileLock from a directory entry and the
* parent directory MSFileLock pointer. It looks if it already has a Lock
* on it. In that case, it simply increments its reference count, when
* possible.
*/
struct MSFileLock *
MakeLock(parentdir, dir, mode)
struct MSFileLock *parentdir;
struct DirEntry *dir;
ulong mode;
{
register struct MSFileLock *fl;
struct MSFileLock *nextfl;
if (mode != EXCLUSIVE_LOCK || (dir->de_Msd.msd_Attributes & ATTR_DIR))
mode = SHARED_LOCK;
#ifdef HDEBUG
debug(("MakeLock: "));
PrintDirEntry(dir);
#endif
/*
* Look through our list to see if we already have it. The criteria
* for this are: 1. the directory entries are the same or 2. they have
* the same first cluster and are both directories (which can have
* multiple directory entries). Sigh.
*/
for (fl = (struct MSFileLock *) LockList->ll_List.mlh_Head;
nextfl = (struct MSFileLock *) fl->msfl_Node.mln_Succ;
fl = nextfl) {
#ifdef HDEBUG
debug(("> "));
PrintDirEntry(&fl->msfl_Msd);
#endif
if ((fl->msfl_DirSector == dir->de_Sector &&
fl->msfl_DirOffset == dir->de_Offset) ||
(fl->msfl_Msd.msd_Cluster == dir->de_Msd.msd_Cluster &&
(dir->de_Msd.msd_Attributes & ATTR_DIR) &&
(fl->msfl_Msd.msd_Attributes & ATTR_DIR))
) {
/* Found existing lock on file */
if (fl->msfl_Refcount < 0 || mode == EXCLUSIVE_LOCK) {
error = ERROR_OBJECT_IN_USE;
return NULL;
}
fl->msfl_Refcount++;
return fl;
}
}
fl = AllocMem((long) sizeof (*fl), MEMF_PUBLIC);
if (fl == NULL) {
error = ERROR_NO_FREE_STORE;
return NULL;
}
fl->msfl_Parent = parentdir ? MSDupLock(parentdir) : NULL;
fl->msfl_Refcount = (mode == EXCLUSIVE_LOCK) ? -1 : 1;
fl->msfl_DirSector = dir->de_Sector;
fl->msfl_DirOffset = dir->de_Offset;
fl->msfl_Msd = dir->de_Msd;
AddHead(&LockList->ll_List, fl);
return fl;
}
/*
* This routine Locks a file. It first searches it in the directory, then
* lets the rest of the work be done by MakeLock(). If it encounters an
* empty slot in the directory, it remembers where, in case we need it. If
* you clear the MODE_CREATEFILE bit in the mode parameter, we fabricate a
* new MSFileLock from the empty directory entry. It then becomes the
* caller's responsibility to MSUnLock() it eventually.
*/
struct MSFileLock *
MSLock(parentdir, name, mode)
struct MSFileLock *parentdir;
byte *name;
ulong mode;
{
byte *sector;
struct MSFileLock *newlock;
register struct DirEntry *de;
struct DirEntry sde;
byte *nextpart;
byte component[8 + 3]; /* Note: not null-terminated */
int createit;
word freesec;
word freeoffset;
de = &sde;
newlock = NULL;
/*
* See if we have an absolute path name (starting at the root).
*/
{
register byte *colon;
if (colon = index(name, ':')) {
name = colon + 1;
parentdir = RootLock;
/*
* MSH::Command or ::Command?
*/
if (name[0] == ':') {
HandleCommand(name);
error = ERROR_OBJECT_NOT_FOUND;
return NULL;
}
}
}
/*
* Get a copy of the parent dir lock, so we can walk it over the
* directory tree.
*/
parentdir = MSDupLock(parentdir);
/*
* Start with the directory entry of the parent dir.
*/
sde.de_Msd = parentdir->msfl_Msd;
sde.de_Sector = parentdir->msfl_DirSector;
sde.de_Offset = parentdir->msfl_DirOffset;
#ifdef HDEBUG
debug(("pdir %08lx: ", parentdir));
PrintDirEntry(&parentdir->msfl_Msd);
#endif
newdir:
freesec = SEC_EOF; /* Means none found yet */
nextpart = ToMSName(component, name);
debug(("Component: '%11s'\n", component));
if (nextpart[0] != '/') {
nextpart = NULL;
#ifndef READONLY
/*
* See if we are requested to get an empty spot in the directory
* if the given name does not exist already. The value of mode is
* not important until we actually create the filelock.
*/
if (!(mode & MODE_CREATEFILE)) {
mode ^= MODE_CREATEFILE;
createit = 1;
} else
createit = 0;
#endif
} else
nextpart++;
/*
* Are we at the end of the name? Then we are finished now. This works
* because sde contains the directory entry of parentdir.
*/
if (name[0] == '\0')
goto exit;
/*
* If there is more name, we enter the directory, and here we get the
* first entry.
*/
sde.de_Sector = DirClusterToSector(sde.de_Msd.msd_Cluster);
sde.de_Offset = 0;
if ((sector = GetSec(sde.de_Sector)) == NULL)
goto error;
CopyMem(sector, &sde.de_Msd, (long) sizeof (struct MsDirEntry));
OtherEndianMsd(&sde.de_Msd);
FreeSec(sector);
while (de) {
switch (CompareNames(&sde.de_Msd, component)) {
case CMP_FREE_SLOT:
if (freesec == SEC_EOF) {
freesec = sde.de_Sector;
freeoffset = sde.de_Offset;
}
/* Fall through */
case CMP_NOT_EQUAL:
de = FindNext(&sde, createit); /* Try next directory
* entry */
continue;
case CMP_OK_DIR:
if (name = nextpart) {
/*
* We want to keep locks on all directories between each
* bottom directory and file lock, so we can easily find
* the parent of a lock. There just seems to be a problem
* here when we enter the 'subdirectories' . or .. , but
* that in not so: MakeLock will detect that it has
* already a lock on those, and NOT make :one/two the
* parent of :one/two/.. .
*/
newlock = MakeLock(parentdir, de, SHARED_LOCK);
MSUnLock(parentdir);
parentdir = newlock;
goto newdir;
}
goto exit;
case CMP_OK_FILE:
if (nextpart) {
error = ERROR_OBJECT_WRONG_TYPE;
de = NULL;
}
goto exit;
case CMP_END_OF_DIR: /* means we will never find it */
error = ERROR_OBJECT_NOT_FOUND;
if (freesec == SEC_EOF) {
freesec = sde.de_Sector;
freeoffset = sde.de_Offset;
}
de = NULL;
goto exit;
}
}
exit:
if (de) {
newlock = MakeLock(parentdir, &sde, mode);
} else {
newlock = NULL;
#ifndef READONLY
if (createit && /* Do we want to make it? */
error == ERROR_OBJECT_NOT_FOUND && /* does it not exist yet? */
nextpart == NULL) { /* do we have the last part of the name */
if (freesec != SEC_EOF) { /* is there any room? */
if (IDDiskState == ID_VALIDATED) {
error = 0;
setmem(&sde.de_Msd, sizeof (sde.de_Msd), 0);
sde.de_Sector = freesec;
sde.de_Offset = freeoffset;
/* ToMSName(sde.de_Msd.msd_Name, name); */
strncpy(sde.de_Msd.msd_Name, component, 8 + 3);
EmptyFileLock = MakeLock(parentdir, &sde, mode);
WriteFileLock(EmptyFileLock);
} else
error = ERROR_DISK_WRITE_PROTECTED;
} else
error = ERROR_DISK_FULL;
}
#endif
}
error:
MSUnLock(parentdir);
return newlock;
}
/*
* This routine DupLocks a file. This simply means incrementing the
* reference count, if it was not an exclusive Lock.
*/
struct MSFileLock *
MSDupLock(fl)
struct MSFileLock *fl;
{
if (fl == NULL)
fl = RootLock;
if (fl->msfl_Refcount <= 0) {
error = ERROR_OBJECT_IN_USE;
return NULL;
} else {
fl->msfl_Refcount++;
}
return fl;
}
/*
* This routine DupLocks the parent of a lock, if there is one.
*/
struct MSFileLock *
MSParentDir(fl)
register struct MSFileLock *fl;
{
if (fl == NULL || fl == RootLock) {
error = ERROR_OBJECT_NOT_FOUND;
} else if (fl->msfl_Parent)
return MSDupLock(fl->msfl_Parent);
return NULL;
}
/*
* This routine UnLocks a file.
*/
int
MSUnLock(fl)
struct MSFileLock *fl;
{
#ifdef HDEBUG
debug(("MSUnLock %08lx: ", fl));
PrintDirEntry(&fl->msfl_Msd);
#endif
if (fl) {
if (--fl->msfl_Refcount <= 0) {
struct LockList *list;
list = (struct LockList *) fl->msfl_Node.mln_Pred;
Remove(fl);
debug(("Remove()d %08lx\n", fl));
/*
* We may need to get rid of the LockList if it is empty. This
* is the current LockList iff we are called from
* MSDiskRemoved(). Please note that we are not even sure that
* 'list' really is the list header, therefore the careful
* test if fl refers to a volume label (root lock) which is
* finally UnLock()ed. Because of the recursion, we only try to
* free the LockList iff there is no parent anymore, since
* otherwise list may be invalid by the time we use it.
*/
if (fl->msfl_Parent) {
MSUnLock(fl->msfl_Parent);
} else {
if ((fl->msfl_Msd.msd_Attributes & ATTR_VOLUMELABEL) &&
((void *) list->ll_List.mlh_Head ==
(void *) &list->ll_List.mlh_Tail)
) {
FreeLockList(list);
}
}
FreeMem(fl, (long) sizeof (*fl));
}
}
return DOSTRUE;
}
/*
* This is (among other things) the inverse of ToMSName().
*/
void
ExamineDirEntry(msd, fib)
struct MsDirEntry *msd;
register struct FileInfoBlock *fib;
{
#ifdef HDEBUG
debug(("+ "));
PrintDirEntry(msd);
#endif
/*
* Special treatment when we examine the root directory
*/
if (fib->fib_DiskKey == (long)ROOT_SEC << 16) {
strncpy(&fib->fib_FileName[1], msd->msd_Name, 8 + 3);
(void) ZapSpaces(&fib->fib_FileName[2], &fib->fib_FileName[1 + 8 + 3]);
} else {
register byte *end,
*dot;
strncpy(&fib->fib_FileName[1], msd->msd_Name, 8);
/* Keep at least one character, even a space, before the dot */
dot = ZapSpaces(&fib->fib_FileName[2], &fib->fib_FileName[1 + 8]);
dot[0] = ' ';
strncpy(dot + 1, msd->msd_Ext, 3);
dot[4] = '\0';
end = ZapSpaces(dot, dot + 4);
if (end > dot)
dot[0] = '.';
}
fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]);
fib->fib_EntryType =
fib->fib_DirEntryType =
(msd->msd_Attributes & ATTR_DIR) ? FILE_DIR : FILE_FILE;
fib->fib_Protection = 0;
if (!(msd->msd_Attributes & ATTR_ARCHIVED))
fib->fib_Protection |= FIBF_ARCHIVE;
if (msd->msd_Attributes & ATTR_READONLY)
fib->fib_Protection |= (FIBF_WRITE | FIBF_DELETE);
if (msd->msd_Attributes & (ATTR_HIDDEN|ATTR_SYSTEM))
fib->fib_Protection |= FIBF_HIDDEN;
fib->fib_Size = msd->msd_Filesize;
fib->fib_NumBlocks = (msd->msd_Filesize + Disk.bps - 1) / Disk.bps;
ToDateStamp(&fib->fib_Date, msd->msd_Date, msd->msd_Time);
fib->fib_Comment[0] = 0;
}
/*
* We remember what we should do when we call ExNext with a lock on
* a directory (enter or step over it) by a flag in fib_EntryType.
* Unfortunately, the Commodore (1.3) List and Dir commands expect
* that fib_EntryType contains the information that the documentation
* (libraries/dos.h) specifies to be in fib_DirEntryType. Therefore
* we use the low bit in fib_DiskKey instead. Yech.
*/
int
MSExamine(fl, fib)
struct MSFileLock *fl;
register struct FileInfoBlock *fib;
{
if (fl == NULL)
fl = RootLock;
fib->fib_DiskKey = ((ulong) fl->msfl_DirSector << 16) |
fl->msfl_DirOffset +
1; /* No ExNext called yet */
ExamineDirEntry(&fl->msfl_Msd, fib);
return DOSTRUE;
}
int
MSExNext(fl, fib)
struct MSFileLock *fl;
register struct FileInfoBlock *fib;
{
word sector = fib->fib_DiskKey >> 16;
word offset = (word) fib->fib_DiskKey;
byte *buf;
if (fl == NULL)
fl = RootLock;
if (offset & 1) {
if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
/* Enter subdirectory */
sector = DirClusterToSector(fl->msfl_Msd.msd_Cluster);
offset = 0;
} else {
offset--; /* Remember, it was odd */
NextDirEntry(§or, &offset);
}
} else {
skip:
NextDirEntry(§or, &offset);
}
if (sector != SEC_EOF) {
struct MsDirEntry msd;
if (buf = GetSec(sector)) {
msd = *(struct MsDirEntry *) (buf + offset);
FreeSec(buf);
if (msd.msd_Name[0] == '\0') {
goto end;
}
if (msd.msd_Name[0] & DIR_DELETED_MASK ||
msd.msd_Name[0] == '.' || /* Hide "." and ".." */
(msd.msd_Attributes & ATTR_VOLUMELABEL)) {
goto skip;
}
OtherEndianMsd(&msd); /* Get correct endianness */
fib->fib_DiskKey = ((ulong) sector << 16) | offset;
ExamineDirEntry(&msd, fib);
return DOSTRUE;
}
}
end:
error = ERROR_NO_MORE_ENTRIES;
return DOSFALSE;
}
/*
* Convert AmigaDOS protection bits to messy attribute bits.
*/
long
MSSetProtect(parentdir, name, mask)
register struct MSFileLock *parentdir;
char *name;
long mask;
{
register struct MSFileLock *lock;
if (parentdir == NULL)
parentdir = RootLock;
lock = MSLock(parentdir, name, EXCLUSIVE_LOCK);
if (lock) {
/* Leave SYSTEM bit as-is */
lock->msfl_Msd.msd_Attributes &= ATTR_SYSTEM;
/* write or delete protected -> READONLY */
if (mask & (FIBF_WRITE|FIBF_DELETE))
lock->msfl_Msd.msd_Attributes |= (ATTR_READONLY);
/* hidden -> hidden */
if (mask & FIBF_HIDDEN)
lock->msfl_Msd.msd_Attributes |= (ATTR_HIDDEN);
/* archived=0 (default) -> archived=1 (default) */
if (!(mask & FIBF_ARCHIVE))
lock->msfl_Msd.msd_Attributes |= (ATTR_ARCHIVED);
WriteFileLock(lock);
MSUnLock(lock);
return TRUE;
}
return FALSE;
}
int
CheckLock(lock)
register struct MSFileLock *lock;
{
register struct MSFileLock *parent;
if (lock) {
while (parent = lock->msfl_Parent)
lock = parent;
if (lock != RootLock)
error = ERROR_DEVICE_NOT_MOUNTED;
}
return error;
}
#ifndef READONLY
void
WriteFileLock(fl)
register struct MSFileLock *fl;
{
debug(("WriteFileLock %08lx\n", fl));
if (fl) {
register byte *block = GetSec(fl->msfl_DirSector);
if (block) {
CopyMem(&fl->msfl_Msd, block + fl->msfl_DirOffset,
(long) sizeof (fl->msfl_Msd));
OtherEndianMsd(block + fl->msfl_DirOffset);
MarkSecDirty(block);
FreeSec(block);
}
}
}
void
UpdateFileLock(fl)
register struct MSFileLock *fl;
{
struct DateStamp dateStamp;
debug(("UpdateFileLock %08lx\n", fl));
DateStamp(&dateStamp);
ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, &dateStamp);
WriteFileLock(fl);
}
#endif
struct LockList *
NewLockList(cookie)
void *cookie;
{
struct LockList *ll;
if (ll = AllocMem((long) sizeof (*ll), MEMF_PUBLIC)) {
NewList(&ll->ll_List);
ll->ll_Cookie = cookie;
} else
error = ERROR_NO_FREE_STORE;
return ll;
}
void
FreeLockList(ll)
struct LockList *ll;
{
debug(("FreeLockList %08lx\n", ll));
if (ll) {
MayFreeVolNode(ll->ll_Cookie); /* not too happy about this */
FreeMem(ll, (long) sizeof (*ll));
if (ll == LockList) /* locks on current volume */
LockList = NULL;
}
}